﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;
using Smarly.Core;
using Smarly.Core.Database;

namespace Smarly.Ebook.Model
{

	public class EBookTree 
	{

		public class Book
		{

			internal Guid BookId { get; set; }

			public string Author { get; set; }
			public string CoverUrl { get; set; }

			public EBookTree Root { get; set; }

			public string HashTag { get; set; }
			public string Title { get; set; }
		}


		public long Id { get; set; }

		internal long ParentId { get; set; }
		public EBookTree Parent { get; set; }
		public List<EBookTree> Children { get; set; }

		internal Guid BookId { get; set; }
		public Book CurrentBook { get; set; }

		internal int LeftKey { get; set; }

		public byte Level { get; set; }

		public string Url { get; set; }
		public bool IsHead { get; set; }

		public string Text { get; set; }

		public bool Published { get; set; }

		public string HashTag { get; set; }

		public DateTime UpdateDate { get; set; }

		#region TextItem

		public TextItem Ti { get; set; }

		public class TextItem
		{

			public Tag Tag { get; set; }
			public string InnerText { get; set; }
			public List<EBookTree.TextItem> Items { get; set; }

/*
			public class Properties
			{
				public string Src { get; set; }
				public string Class { get; set; }
				public long SrcChapter { get; set; }
			}
*/

		}

		#endregion


		public EBookTree Get(SmarlyIdentity identity, string url, bool fullDeep)
		{
			Url = url;
			var result = SqlRepository.ExecuteProcedureReader("Text_BookTree_Get", new List<SpParam>
				                                                                                       {
					                                                                                       new SpParam {Name = "@userId", Value = identity.UserId, Type = DbType.Guid}, 
																																																 new SpParam {Name = "@url", Value = url, Type = DbType.String}, 
																																																 new SpParam {Name = "@fullDeep", Value = fullDeep, Type = DbType.Boolean},
				                                                                                       }, null, ReadGet);

			ProcessBook(result.CurrentBook.Root.Children);

			return result;

		}


		private EBookTree ReadGet(IDataReader reader)
		{
			Book book = null;
			EBookTree result = null;
			int batchNum = 0;
			do
			{
				switch (batchNum)
				{
					case 0:
						while (reader.Read())
						{
							book = new Book
							{
								Author = reader.GetString("Author"),
								CoverUrl = reader.GetString("CoverUrl"),
							};

						}
						break;
					case 1:
						List<EBookTree> list = new List<EBookTree>();
						while (reader.Read())
						{
							var item = new EBookTree
							{
								Id = reader.GetInt64("BookTreeId"),
								ParentId = reader.GetInt64OrNull("ParentBookTreeId") ?? -1,
								LeftKey = reader.GetInt32("LeftKey"),
								Level = reader.GetByte("Level"),
								IsHead = reader.GetBoolean("IsHead"),
								Text = reader.GetString("Text"),
								Url = reader.GetStringOrNull("Url"),
								Published = reader.GetBoolean("Published"),
								HashTag = reader.GetStringOrNull("HashTag"),
								CurrentBook = book,
							};

/*
							if (!item.IsHead)
								item.Text = IndesignPocess.Process(item.Text);
*/
//								item.Ti = Parse(item.Text);

							list.Add(item);

							if (item.Url == Url)
								result = item;
						}

						EBookTree root = list.Single(x => x.ParentId == -1);
						CreateTree(root, list);

						book.Root = root;


						break;
					default:
						throw new ApplicationException("Additional batch select");

				}
				batchNum++;
			} while (reader.NextResult());

			return result;
		}

		private void ProcessBook(List<EBookTree> children)
		{
			for (var i = 0; i < children.Count; i++)
			{
				EBookTree prev = i == 0 ? null : children[i - 1];
				EBookTree next = (i + 1 < children.Count) ? children[i + 1] : null;

				EBookTree child = children[i];

				if (!child.IsHead)
					child.Text = IndesignPocess.Process(child.Text, prev, next);

				if (child.Children != null)
					ProcessBook(child.Children);

			}
		}

		protected static readonly Regex Br = new Regex(@"(<br[^>]*/{0,1}>)", RegexOptions.IgnoreCase);
		public static EBookTree.TextItem Parse(string str)
		{
			str = Br.Replace(str, "[[br]]");
			XDocument doc = XDocument.Parse(str, LoadOptions.PreserveWhitespace);
			string postfix = string.Empty;
			var result = GetBlock(doc.Root, null, ref postfix);
			result.Items.Add(result);

			return result;
		}


		private static string InnerXmlValue(XElement root)
		{
			if (root.Value == string.Empty)
				return string.Empty;

			var doc = new XDocument(root).Root;
			doc.Value = ";";
			var array = doc.ToString(SaveOptions.DisableFormatting).Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			string result = root.ToString(SaveOptions.DisableFormatting);
			result = result.Substring(0, result.Length - array[1].Length).Substring(array[0].Length);
			return result;
		}
		private static readonly Regex EndSpaces = new Regex(@"(\u0020|\u00A0){1,}$", RegexOptions.None);

		private static TextItem GetBlock(XElement root, List<TextItem> list, ref string postFix)
		{
			var result = new EBookTree.TextItem() { /*Id = root.ElementId() */};
			result.Items = list ?? new List<TextItem>();

			try
			{
				result.Tag = (Tag)Enum.Parse(typeof(Tag), root.Name.ToString());
			}
			catch
			{
				switch (root.Name.ToString())
				{
					case "i":
						result.Tag = Tag.em;
						break;
					case "b":
						result.Tag = Tag.strong;
						break;
					default:
						throw;
				}
			}
//			result.ExtData = ParseAttributes(root, result.Tag);

			string rootStr = InnerXmlValue(root);
			foreach (XElement node in root.Elements())
			{
				TextItem child = GetBlock(node, result.Items, ref postFix);
				result.Items.Add(child);

				//rootStr = rootStr.Replace(node.ToString(SaveOptions.DisableFormatting), "<text id='" + child.Id + "'/>" + postFix);
			}

			postFix = EndSpaces.Match(rootStr).Value;
			rootStr = EndSpaces.Replace(rootStr, string.Empty);

			switch (result.Tag)
			{
				case Tag.pre:
					rootStr = rootStr.TrimStart('\r', '\n').Replace(" ", " ").Replace("    ", "\t").TrimEnd();
					break;
			}


			result.InnerText = rootStr.Replace("[[br]]", "<br />");

			result.InnerText = TrimSpaces(result.InnerText);


			return result;
		}

		protected static readonly Regex NonBreakingSpaces = new Regex(@"\u0020*\u00A0+\u0020*", RegexOptions.None);
		protected static readonly Regex BeginSpaces = new Regex(@"^(\u0020|\u00A0){1,}", RegexOptions.None);
		protected static readonly Regex DoubleSpaces = new Regex(@"\u0020+", RegexOptions.None);

		private static string TrimSpaces(string str)
		{
			str = NonBreakingSpaces.Replace(str, /*nbs*/" ");
			str = BeginSpaces.Replace(str, string.Empty);
			/*
						if (DoubleSpaces.Matches(str).Cast<Match>().Any(match => string.IsNullOrWhiteSpace(match.Value)))
						{
							var l = DoubleSpaces.Matches(str).Cast<Match>().Where(match => string.IsNullOrWhiteSpace(match.Value)).ToList();
							throw new ApplicationException("Херня ваш \\s");
						}
			*/

			str = DoubleSpaces.Replace(str, " ");
			return str;
		}
/*
		private static TextItem1.Properties ParseAttributes(XElement el, Tag tag)
		{
			TextItem1.Properties result = null;

			switch (tag)
			{
				case Tag.img:
					var attribute = el.SingleAttributeValue("src");

					try
					{
						new Uri(attribute);
					}
					catch
					{
						attribute = "/" + attribute.TrimStart('.', '/');
					}
					result = new TextItem1.Properties
					{
						Src = attribute,
					};
					try
					{
						string @class = el.SingleOrDefaultAttributeValue("class");
						result = result ?? new TextItem1.Properties();
						result.Class = @class;
					}
					catch
					{
					}

					break;
				case Tag.pre:
					try
					{
						var @class = el.SingleOrDefaultAttributeValue("class");
						result = new TextItem1.Properties
						{
							Class = @class,
						};
					}
					catch
					{
					}
					break;
				case Tag.a:
					result = new TextItem1.Properties()
					{
						Src = el.SingleAttributeValue("href").TrimStart('#'),
					};
					break;

			}
			return result;
		}
*/


		public static void CreateTree(EBookTree root, List<EBookTree> list)
		{
			if (root == null || list == null)
				return;

			root.Children = list.Where(x => x.ParentId == root.Id).OrderBy(x => x.LeftKey).ToList();

			if (root.Children != null)
				foreach (var child in root.Children)
				{
					child.Parent = root;
					CreateTree(child, list);
				}
		}




	}
}